package com.hdu.lisener.core;


import com.hdu.lisener.holder.ApplicationContextHolder;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;


import java.util.*;

import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;

/**
 * 事件派发器
 */

@Component
public class EventDispatcher implements SmartInitializingSingleton {


    @SuppressWarnings("all")
    public final Map<Class<?>, List<ApplicationListener>> ALL_LISTENERS = new HashMap<>();


    @SuppressWarnings("all")
    public void dispatchEvent(ApplicationEvent event) {
        ALL_LISTENERS.getOrDefault(event.getClass(), Collections.emptyList())
                .forEach(listener -> listener.onApplicationEvent(event));
    }


    @Override
    public void afterSingletonsInstantiated() {
        handleClass();
        handleMethod();
    }


    @SuppressWarnings("all")
    private void handleClass() {
        Collection<ApplicationListener> applicationListeners = ApplicationContextHolder
                .getBeansOfType(ApplicationListener.class).values();

        for (ApplicationListener<?> listener : applicationListeners) {
            ResolvableType rt = ResolvableType.forClass(listener.getClass());
            ResolvableType lisenerType = rt.as(ApplicationListener.class);
            if (lisenerType != ResolvableType.NONE) {
                // 泛型已经实例化
                Class<?> eventType = lisenerType.getGeneric(0).getRawClass();
                ALL_LISTENERS.putIfAbsent(eventType, new ArrayList<>());
                ALL_LISTENERS.get(eventType).add(listener);
            }
        }
    }

    @SuppressWarnings("all")
    private void handleMethod() {
        Collection<Object> allBean = ApplicationContextHolder
                .getBeansOfType(Object.class).values();
        for (Object bean : allBean) {
            Class<?> clazz = bean.getClass();
            ReflectionUtils.doWithMethods(clazz, method -> {
                EventListener eventListener = null;
                if ((eventListener = findAnnotation(method, EventListener.class)) != null) {
                    Class<? extends ApplicationEvent> eventType = eventListener.value();
                    ALL_LISTENERS.putIfAbsent(eventType, new ArrayList<>());
                    ALL_LISTENERS.get(eventType).add(new ApplicationListenerAdapter(bean, method, eventType));
                }
            });
        }
    }
}
